'use server';

import { getSession } from '@/lib/auth';
import { ORDER_PAY_EXPIRE } from '@/lib/constants';
import { db } from '@/lib/db';
import { order } from '@/lib/db/schema/common';
import { gorse } from '@/lib/gorse';
import redis, { genNewOrderNotOnlineKey } from '@/lib/redis';
import { checkOnline, sendSocketData, socketsMap } from '@/lib/socket';
import { result, withZod } from '@/lib/utils';
import { and, eq, inArray, notInArray } from 'drizzle-orm';
import { revalidatePath } from 'next/cache';
import { z } from 'zod';
import { BizError, ErrorCode } from '../error';
import { getProductsByIds } from '../query/product';
import { NEW_ORDER_EVENT, type NewOrderNotification } from '../type/socket';
import { clearCart } from './cart';

export const createOrder = withZod(
  z.object({
    cart: z.record(z.string(), z.number()),
    shopId: z.number(),
    remark: z.string().optional(),
    address: z.object({
      address: z.string(),
      name: z.string(),
      phone: z.string(),
    }),
  }),
  async ({ cart, shopId, address, remark }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      const res = await db
        .insert(order)
        .values({
          shopId,
          userId: session.user.id,
          userName: address.name,
          userPhone: address.phone,
          address: address.address,
          products: Object.entries(cart).map(([id, count]) => ({
            id: Number(id),
            count,
          })),
          remark,
        })
        .returning({ insertedId: order.id });
      clearCart(shopId);
      if (!res[0]?.insertedId)
        throw new BizError(ErrorCode.NotFound, '创建订单失败');
      return result(res[0].insertedId);
    } catch (error) {
      return result(error, '创建订单失败,请重试');
    }
  },
);

export const checkUnpaidOrder = withZod(
  z.object({
    orderId: z.number(),
  }),
  async ({ orderId }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      const orderData = await db.query.order.findFirst({
        columns: {
          status: true,
          createdAt: true,
        },
        where: and(eq(order.id, orderId), eq(order.userId, session.user.id)),
      });
      if (!orderData) throw new BizError(ErrorCode.NotFound, '订单不存在');
      if (orderData.status !== 'unpaid')
        throw new BizError(ErrorCode.ActionFailed, '订单状态错误');
      const remainSeconds =
        ORDER_PAY_EXPIRE -
        Math.floor(
          (new Date().getTime() - orderData.createdAt.getTime()) / 1000,
        );
      if (remainSeconds <= 0) {
        await db
          .update(order)
          .set({ status: 'canceled' })
          .where(and(eq(order.id, orderId), eq(order.userId, session.user.id)));
      }
      revalidatePath('/order');
      revalidatePath(`/order/${orderId}`);
      return result();
    } catch (error) {
      return result(error, '更新失败,请重试');
    }
  },
);

export const cancelOrder = withZod(
  z.object({
    orderId: z.number(),
  }),
  async ({ orderId }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      await db
        .update(order)
        .set({ status: 'canceled' })
        .where(
          and(
            eq(order.id, orderId),
            eq(order.userId, session.user.id),
            inArray(order.status, ['unpaid', 'unaccepted']),
          ),
        );
      revalidatePath('/order');
      revalidatePath(`/order/${orderId}`);
      return result();
    } catch (error) {
      return result(error, '更新失败,请重试');
    }
  },
);

export const payOrder = withZod(
  z.object({
    orderId: z.number(),
  }),
  async ({ orderId }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      await db
        .update(order)
        .set({ status: 'unaccepted' })
        .where(
          and(
            eq(order.id, orderId),
            eq(order.userId, session.user.id),
            eq(order.status, 'unpaid'),
          ),
        );

      const orderData = await db.query.order.findFirst({
        columns: {
          address: true,
          createdAt: true,
          products: true,
          shopId: true,
        },
        with: {
          shop: {
            columns: {
              userId: true,
            },
          },
        },
        where: and(eq(order.id, orderId), eq(order.userId, session.user.id)),
      });
      if (!orderData) throw new BizError(ErrorCode.NotFound, '订单不存在');

      gorse.insertFeedbacks([
        {
          itemId: orderData.shopId.toString(),
          userId: session.user.id,
          type: 'order',
        },
      ]);

      const { result: products, error } = await getProductsByIds(
        orderData?.products.map((product) => product.id) || [],
      );
      if (error) throw error;

      const isShopOnline = await checkOnline(orderData.shop.userId);

      const notifyData: NewOrderNotification = {
        id: orderId,
        address: orderData.address,
        createdAt: orderData.createdAt,
        imageKeys: products
          .map((product) => product.imageKeys[0])
          .filter(Boolean),
      };

      isShopOnline &&
        sendSocketData<NewOrderNotification>({
          event: NEW_ORDER_EVENT,
          toIds: [orderData.shop.userId],
          data: notifyData,
        });

      if (!isShopOnline) {
        const key = genNewOrderNotOnlineKey({
          userId: session.user.id,
          shopUserId: orderData.shop.userId,
        });
        const dataJSON = await redis.get(key);
        const data = JSON.parse(dataJSON || '[]');
        data.push(notifyData);
        redis.setEx(key, 60 * 60 * 24, JSON.stringify(data));
      }

      revalidatePath('/order');
      revalidatePath(`/order/${orderId}`);
      return result();
    } catch (error) {
      return result(error, '更新失败,请重试');
    }
  },
);

export const shopUpdateOrderStatus = withZod(
  z.object({
    orderId: z.number(),
    status: z.enum(['pending', 'delivering', 'completed']),
  }),
  async ({ orderId, status }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      if (session.user.role !== 'shop')
        throw new BizError(ErrorCode.Unauthorized, '仅商家可操作');
      const shopData = await db.query.shop.findFirst({
        columns: {
          id: true,
        },
        where: (shop, { eq }) => eq(shop.userId, session.user.id),
      });
      if (!shopData) throw new BizError(ErrorCode.NotFound, '商家不存在');
      await db
        .update(order)
        .set({ status })
        .where(
          and(
            eq(order.id, orderId),
            eq(order.shopId, shopData.id),
            inArray(order.status, ['unaccepted', 'pending', 'delivering']),
          ),
        );
      revalidatePath('/shop/order');
      revalidatePath(`/shop/order/${orderId}`);
      return result();
    } catch (error) {
      return result(error, '更新失败,请重试');
    }
  },
);

export const evaluateOrder = withZod(
  z.object({
    orderId: z.number(),
    deliverRate: z.number().int().min(1).max(5),
    productRate: z.number().int().min(1).max(5),
  }),
  async ({ orderId, deliverRate, productRate }) => {
    try {
      const session = await getSession();
      if (!session?.user)
        throw new BizError(ErrorCode.Unauthorized, '用户未登录');
      await db
        .update(order)
        .set({
          evaluation: {
            deliver: deliverRate,
            product: productRate,
          },
        })
        .where(
          and(
            eq(order.id, orderId),
            eq(order.userId, session.user.id),
            eq(order.status, 'completed'),
          ),
        );
      revalidatePath(`/order/${orderId}`);
      return result();
    } catch (error) {
      return result(error, '更新失败,请重试');
    }
  },
);
